package com.example.springboot.config.shiro;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.example.springboot.entity.User;
import com.example.springboot.service.UserService;
import com.example.springboot.util.RedisUtil;
import com.example.springboot.util.TokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import java.util.HashSet;
import java.util.Set;

@Slf4j
public class CustomRealm extends AuthorizingRealm {

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private UserService userService;

    /**
     * 必须重写此方法，jwt和shiro绑定
     * @param token
     * @return
     */
    @Override
    public boolean supports(AuthenticationToken token) {
//        return super.supports(token);
        return token instanceof JWTToken;
    }


    /**
     * 权限认证
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 从token中获取用户信息
        String primaryPrincipal = (String)principalCollection.getPrimaryPrincipal();
        String userName = TokenUtil.getUsername(primaryPrincipal);
        // 通过用户信息获取角色权限
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Set<String> roles = new HashSet<>();
        roles.add("user");
        roles.add("admin");
        info.setRoles(roles);
        Set<String> permissionSet = new HashSet<>();
        permissionSet.add("user:show");
        permissionSet.add("user:admin");
        info.setStringPermissions(permissionSet);
        return info;
    }

    /**
     * 身份认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // shiro 校验
//        String userName = (String)authenticationToken.getPrincipal();
//        if(StringUtils.isEmpty(userName)){
//            throw new AccountException("用户名不正确");
//        }
//        // 数据库查询出来的密码 用户名和密码 123/123
//        String password = "f5a7977a18cabb413b540b9166435f26";
//        ByteSource bytes = ByteSource.Util.bytes(userName + "salt");
//        // 密码校验由：HashedCredentialsMatcher.doCredentialsMatch token, info。（token：用户登入时创建，密码传入时是明文，比较时会自动加密；info 由当前函数返回）
//        return new SimpleAuthenticationInfo(userName, password, bytes, getName());

        String token = (String)authenticationToken.getPrincipal();
        // 获取参数用户名
        String usernameSource = TokenUtil.getUsername(token);
        // 获取参数密码
        String passwordSource = (String)authenticationToken.getCredentials();

        // 校验用户名
        if(StringUtils.isEmpty(usernameSource)){
            throw new AuthenticationException("token非法无效");
        }
        QueryWrapper<User> queryWrapper = Wrappers.query();
        queryWrapper.eq("username", usernameSource);
        User user = userService.getOne(queryWrapper);
        if(user == null){
            throw new AuthenticationException("用户不存在");
        }

        // 使用登入返回的token，从redis中取出refreshToken，如果为空用当前token赋值
        String refreshToken = (String)redisUtil.get(RedisUtil.USER_TOKEN + user.getUsername());
        if(StringUtils.isEmpty(refreshToken)){
            refreshToken = token;
        }

        // 校验refreshToken是否正确（过期等）
        if(!TokenUtil.verify(refreshToken, usernameSource, passwordSource)){
            // 校验失败，删除redis中的token
            redisUtil.del(RedisUtil.USER_TOKEN + user.getUsername());
            throw new AuthenticationException("用户名或密码错误");
        }else {
            // 校验成功，获取新的refreshToken，放入到redis中，防止refreshToken过期
            User userToken = new User();
            userToken.setUsername(usernameSource);
            userToken.setPassword(passwordSource);
            refreshToken = TokenUtil.getRefreshToken(userToken);
            redisUtil.set(RedisUtil.USER_TOKEN + user.getUsername(), refreshToken, TokenUtil.REFRESH_EXPIRE_TIME / 1000);
        }
        // 数据库存的密码
        String password = user.getPassword();
        ByteSource credentialsSalt = ByteSource.Util.bytes(usernameSource + "salt");
        // HashedCredentialsMatcher hashProvidedCredentials
        return new SimpleAuthenticationInfo(token, password, credentialsSalt, getName());
    }

    /**
     * 密码加密  加密规则shiro定义的规则一样，不一样时，生成的密码无效
     * @param username
     * @param password
     * @return
     */
    public static String MD5Password(String username, String password){
        String md5Pwd = new SimpleHash("MD5", password, ByteSource.Util.bytes(username + "salt"), 2).toHex();
        return md5Pwd;
    }


    public static void main(String[] args) {
        System.out.println(MD5Password("张三","123456"));

//        User user = new User();
//        user.setUsername("张三");
//        user.setPassword("123456");
//        String token = TokenUtil.getToken(user);
//        System.out.println(token);
    }
}
